
import { readFile, PMap, createPMap } from "./utility"


export enum Status {
    B = 0,
    E = 1,
    M = 2,
    S = 3,
    Sum = 4
}
/* @internal */
export interface HmmModel {
    startProb: Array<number>
    transProb: Array<Array<number>>
    emitProbVec: Array<PMap<number>>
    getEmitProb(map: PMap<number>, key: number): number
}
/* @internal */
export function createHmmModel(modelPath: string): HmmModel {
    try {
        if (!content) {
            content = readFile(modelPath).replace(/(\n\r|\n)/g, "\n").split("\n")
        }

    } catch (e) {
        throw ("HMMModel数据文件内容丢失")
    }

    if (!content) {
        throw ("HMMModel数据文件内容丢失")
    }

    loadModel()

    return {
        startProb,
        transProb,
        emitProbVec,
        getEmitProb
    }

}


let content: string[]
let i = 0
let line: string = ""

function NextLine() {

    function getLine() {
        line = content[i]
        i++
        return line

    }

    while (getLine()) {

        line = line.trim()

        if (!line) {
            continue
        }

        if (line.indexOf("#") !== -1) {
            continue
        }

        return true

    }

    return false
}

const statMap: Array<Status> = [Status.B, Status.E, Status.M, Status.S]
const startProb: Array<number> = []
const transProb: Array<Array<number>> = []
const emitProbB: PMap<number> = createPMap<number>()
const emitProbE: PMap<number> = createPMap<number>()
const emitProbM: PMap<number> = createPMap<number>()
const emitProbS: PMap<number> = createPMap<number>()
const emitProbVec: Array<PMap<number>> = [emitProbB, emitProbE, emitProbM, emitProbS]

function loadModel() {

    let tmp: string[]
    let tmp2: string[]

    if (!NextLine()) {
        throw ("HMMModel数据文件内容加载,出现错误. ")
    }

    tmp = line.split(" ")

    if (tmp.length !== Status.Sum) {
        throw ("HMMModel数据文件内容加载,出现错误. ")

    }

    for (let j = 0; j < tmp.length; j++) {

        if (isNaN(+tmp[j])) {
            throw ("HMMModel数据文件内容出现错误. ")

        }
        startProb[j] = +tmp[j]

    }

    for (let i = 0; i < Status.Sum; i++) {

        if (!NextLine()) {

            throw ("HMMModel数据文件内容加载,出现错误. ")

        }
        tmp = line.split(" ")
        if (tmp.length !== Status.Sum) {

            throw ("HMMModel数据文件内容加载,出现错误. ")

        }

        transProb[i] = []
        for (let j = 0; j < Status.Sum; j++) {

            if (isNaN(+tmp[j])) {
                throw ("HMMModel数据文件内容出现错误. ")

            }

            transProb[i][j] = +tmp[j]
        }
    }

    if (!NextLine()) {
        throw ("HMMModel数据文件内容加载,出现错误. ")

    }

    loadEmitProb(line, emitProbB)

    if (!NextLine()) {

        throw ("HMMModel数据文件内容加载,出现错误. ")

    }

    loadEmitProb(line, emitProbE)

    if (!NextLine()) {

        throw ("HMMModel数据文件内容加载,出现错误. ")

    }

    loadEmitProb(line, emitProbM)

    if (!NextLine()) {
        throw ("HMMModel数据文件内容加载,出现错误. ")

    }

    loadEmitProb(line, emitProbS)

    if (!(startProb && startProb.length || transProb && transProb.length
        || emitProbB || emitProbE || emitProbM || emitProbS)) {
        throw ("HMMModel数据文件内容加载,出现错误. ")

    }
}

function loadEmitProb(li: string, map: PMap<number>) {
    if (!li) {
        return false
    }

    let tmp: string[]
    let tmp2: string[]
    tmp = li.split(",")

    for (let i = 0; i < tmp.length; i++) {

        tmp2 = tmp[i].split(":")
        if (tmp2.length !== 2) {
            return false

        }
        map[tmp2[0].charCodeAt(0)] + tmp2[1]

    }
    return true
}

const MIN_DOUBLE = -3.14e+100

function getEmitProb(ptmap: PMap<number>, key: number) {
    return ptmap[key] || MIN_DOUBLE
}
